package top.kpromise.user.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.kpromise.user.data.Config;
import top.kpromise.user.model.repuest.KongUserInfo;
import top.kpromise.user.model.response.AppInfo;
import top.kpromise.user.model.response.UserPermission;
import top.kpromise.user.model.response.UserTokenModel;
import top.kpromise.user.response.Result;
import top.kpromise.user.service.AppInfoService;
import top.kpromise.user.service.PermissionService;
import top.kpromise.user.service.UserTokenService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@Service
public class KongUtils {

    private final Logger logger = LoggerFactory.getLogger(KongUtils.class.getSimpleName());

    private final UserTokenService userTokenService;
    private final AppInfoService appInfoService;
    private final PermissionService permissionService;

    @Value("${com.kpromise.serverHost}")
    private String SERVER_HOST;

    @Value("${com.kpromise.kongAdmin}")
    private String KONG_ADMIN;

    public KongUtils(UserTokenService userTokenService, AppInfoService appInfoService,
                     PermissionService permissionService) {
        this.userTokenService = userTokenService;
        this.appInfoService = appInfoService;
        this.permissionService = permissionService;
    }

    private String buildAuthorizeJson(String appId, String provisionKey, KongUserInfo kongUserInfo) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            HashMap<String, Object> map = new HashMap<>();
            map.put("client_id", appId);
            map.put("response_type", "code");
            map.put("scope", "userInfo");
            map.put("authenticated_userid", objectMapper.writeValueAsString(kongUserInfo));
            map.put("provision_key", provisionKey);
            return objectMapper.writeValueAsString(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Map<String, Object> loginToKong(String appId, String userId) {
        String url = SERVER_HOST + "/user/oauth2/authorize";
        AppInfo appInfo = appInfoService.getByAppId(appId);
        if (appInfo == null) {
            return Result.data(null, "appId错误");
        }

        ArrayList<UserPermission> permissionList = permissionService.permissionList(userId, appId);
        KongUserInfo kongUserInfo = new KongUserInfo();
        kongUserInfo.setUserId(userId);
        if (permissionList != null) {
            ArrayList<String> userPermission = new ArrayList<>();
            for (UserPermission permission : permissionList) {
                userPermission.add(permission.getPermission());
            }
            kongUserInfo.setPermission(userPermission);
        }

        String jsonString = buildAuthorizeJson(appId, appInfo.getProvisionKey(), kongUserInfo);
        JsonNode jsonObject = postJson(url, jsonString);
        if (jsonObject == null || jsonObject.get("redirect_uri") == null) {
            return Result.data(null, "登录失败");
        }

        String redirectUri = jsonObject.get("redirect_uri").asText();
        if (redirectUri == null) {
            logger.error("获取code失败");
            return Result.data(null, "登录失败");
        }
        String code = redirectUri.split("=")[1];
        if (code == null) {
            logger.error("获取code失败");
            return Result.data(null, "登录失败");
        }
        logger.info("code is " + code);
        Map<String, Object> loginResult = getTokenByCode(userId, appId, appInfo.getAppSecret(), code);
        loginResult.put("permission", kongUserInfo.getPermission());
        return loginResult;
    }

    private String buildGetTokenByCodeJson(String appId, String appSecret, String code) {
        ObjectMapper objectMapper = new ObjectMapper();
        HashMap<String, String> map = new HashMap<>();
        map.put("client_id", appId);
        map.put("client_secret", appSecret);
        map.put("grant_type", "authorization_code");
        map.put("code", code);
        try {
            return objectMapper.writeValueAsString(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Map<String, Object> getTokenByCode(String userId, String appId, String appSecret, String code) {
        String url = SERVER_HOST + "/user/oauth2/token";
        String jsonString = buildGetTokenByCodeJson(appId, appSecret, code);
        JsonNode jsonNode = postJson(url, jsonString);
        if (jsonNode == null || !jsonNode.has("refresh_token")) {
            return Result.data(null, "登录失败");
        }
        return Result.data(buildResponseAndSaveOrUpdateToken(jsonNode, userId, appId, true), "登录成功");
    }

    private JsonNode postJson(String url, String jsonString) {
        if (jsonString == null) {
            return null;
        }
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonString);
        Request request = new Request.Builder().post(body).url(url).build();
        JsonNode jsonObject = OkHttpUtils.send(request);
        logger.info("jsonObject " + jsonObject);
        return jsonObject;
    }

    public void logout(String appId, String userId) {
        UserTokenModel userTokenModel = userTokenService.getToken(userId, appId);
        if (userTokenModel != null) {
            logout(userTokenModel.getAccessToken());
        }
    }

    private void logout(String accessToken) {
        if (accessToken == null) return;
        String url = KONG_ADMIN + "/oauth2_tokens/" + accessToken;
        Request request = new Request.Builder().delete().url(url).build();
        OkHttpUtils.send(request);
    }

    private String buildRefreshTokenJson(String clientId, String appSecret, String refreshToken) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("grant_type", "refresh_token");
        map.put("client_id", clientId);
        map.put("client_secret", appSecret);
        map.put("refresh_token", refreshToken);
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Map<String, String> buildResponseAndSaveOrUpdateToken(JsonNode jsonNode, String userId,
                                                                  String clientId, boolean logout) {
        HashMap<String, String> token = new HashMap<>();
        token.put("refreshToken", jsonNode.get("refresh_token").asText());
        token.put("tokenType", jsonNode.get("token_type").asText());
        token.put("accessToken", jsonNode.get("access_token").asText());
        token.put("expiresIn", jsonNode.get("expires_in").asText());
        saveOrUpdateToken(userId, clientId, token.get("accessToken"), token.get("refreshToken"), logout);
        return token;
    }

    private void saveOrUpdateToken(String userId, String clientId, String accessToken,
                                   String refreshToken, boolean logout) {
        UserTokenModel oldToken = userTokenService.getToken(userId, clientId);
        if (oldToken != null) {
            if (logout) {
                logout(oldToken.getAccessToken());
            }
            oldToken.setAccessToken(accessToken);
            oldToken.setRefreshToken(refreshToken);
            userTokenService.updateToken(oldToken);
        } else {
            oldToken = new UserTokenModel();
            oldToken.setAccessToken(accessToken);
            oldToken.setRefreshToken(refreshToken);
            oldToken.setClientId(clientId);
            oldToken.setUserId(userId);
            userTokenService.insertToken(oldToken);
        }
    }

    private JsonNode accessTokenInfo(String accessToken) {
        String url = KONG_ADMIN + "/oauth2_tokens/" + accessToken;
        Request request = new Request.Builder().get().url(url).build();
        JsonNode jsonObject = OkHttpUtils.send(request);
        logger.info("jsonObject " + jsonObject);
        return jsonObject;
    }

    public Map<String, Object> refreshToken(String clientId, String refreshToken) {
        String url = SERVER_HOST + "/user/oauth2/token";
        AppInfo appInfo = appInfoService.getByAppId(clientId);
        if (appInfo == null) {
            return Result.data(null, "appId错误");
        }
        String jsonString = buildRefreshTokenJson(clientId, appInfo.getAppSecret(), refreshToken);
        JsonNode jsonNode = postJson(url, jsonString);
        if (jsonNode == null || !jsonNode.has("refresh_token")) {
            return Result.data(null, "refreshToken已过期");
        }
        JsonNode accessTokenInfo = accessTokenInfo(jsonNode.get("access_token").asText());
        if (!accessTokenInfo.has("authenticated_userid")) {
            return Result.data(null, "Token刷新失败");
        }
        return Result.data(buildResponseAndSaveOrUpdateToken(jsonNode, accessTokenInfo.get("authenticated_userid").asText(),
                clientId, false), "Token刷新成功");
    }
}
